#include <string>
#include <algorithm>
#include <functional>
#include <iterator>
#include <cctype>
#include <numeric>
#include <iostream>

using std::copy;
using std::cout;
using std::make_reverse_iterator;
using std::accumulate;
using std::tolower;
using std::all_of;
using std::greater_equal;
using std::less_equal;
using std::equal;
using std::distance;
using std::bind;
using std::not_equal_to;
using std::placeholders::_1;
using std::find_if;
using std::string;
using std::find;
using std::logical_and;
using std::logical_or;
using std::equal_to;
using std::equal;
using std::isupper;
using std::logical_not;
using std::placeholders::_2;

enum METHOD
{
	PUT = 1,
	POST = 2,
	HEAD = 3,
	TRACE = 4,
	DELETE = 5,
	CONNECT = 6,
	OPTIONS = 7
};




void parse(const string &source)
{
	if (source.empty())
		return;

	static string HTTPStr{ "HTTP" };
	static string HTTP10{ "1.0" }, HTTP11{ "1.1" };
	static string lineStr{ "\r\n\r\n" };
	static string PUTStr{ "PUT" };                                         // 3                
	static string POSTStr{ "POST" }, HEADStr{ "HEAD" };                    // 4                     
	static string TRACEStr{ "TRACE" };                                     // 5
	static string DELETEStr{ "DELETE " };                                  // 6
	static string CONNECTStr{ "CONNECT" }, OPTIONSStr{ "OPTIONS" };        // 7
	static string ContentLength{ "content-length" };
	auto iterBegin{ source.cbegin() }, iterEnd{ source.cend() };
	string::const_iterator funBegin, funEnd, iterTemp, targetBegin, targetEnd, bodyBegin{ iterEnd }, bodyEnd{ iterEnd }, versionBegin, versionEnd, headBegin, headEnd, wordBegin, wordEnd;
	int methodNum{}, len{}, bodyLen{}, num{};
	bool success{ true }, hasBody{ false };
	while (iterBegin != iterEnd)
	{
		if ((iterBegin = find_if(iterBegin, iterEnd, bind(not_equal_to<>(), _1, ' '))) == iterEnd || *iterBegin == '\r' || !isupper(*iterBegin))
		{
			success = false;
			break;
		}

		if ((iterTemp = find_if(iterBegin + 1, iterEnd, bind(logical_not<>(), bind(isupper, _1)))) == iterEnd || *iterTemp != ' ')
		{
			success = false;
			break;
		}

		funBegin = iterBegin, funEnd = iterTemp;                                     //Head

		switch (distance(funBegin, funEnd))
		{
		case 3:
			if (!equal(funBegin, funEnd, PUTStr.cbegin(), PUTStr.cend()))
				success = false;
			else
				methodNum = METHOD::PUT;
			break;
		case 4:
			if (equal(funBegin, funEnd, POSTStr.cbegin(), POSTStr.cend()))
				methodNum = METHOD::POST;
			else if (equal(funBegin, funEnd, HEADStr.cbegin(), HEADStr.cend()))
				methodNum = METHOD::HEAD;
			else
				success = false;
			break;
		case 5:
			if (!equal(funBegin, funEnd, TRACEStr.cbegin(), TRACEStr.cend()))
				success = false;
			else
				methodNum = METHOD::TRACE;
			break;
		case 6:
			if (!equal(funBegin, funEnd, DELETEStr.cbegin(), DELETEStr.cend()))
				success = false;
			else
				methodNum = METHOD::DELETE;
			break;
		case 7:
			if (equal(funBegin, funEnd, CONNECTStr.cbegin(), CONNECTStr.cend()))
				methodNum = METHOD::CONNECT;
			else if (equal(funBegin, funEnd, OPTIONSStr.cbegin(), OPTIONSStr.cend()))
				methodNum = METHOD::OPTIONS;
			else
				success = false;
			break;
		default:
			success = false;
			break;
		}

		if (!success)
			break;

		if ((targetBegin = find_if(funEnd + 1, iterEnd, bind(not_equal_to<>(), _1, ' '))) == iterEnd || *targetBegin == '\r')
		{
			success = false;
			break;
		}

		if ((targetEnd = find_if(targetBegin + 1, iterEnd, bind(logical_or<>(), bind(equal_to<>(), _1, ' '), bind(logical_or<>(), bind(equal_to<>(), _1, '?'), bind(equal_to<>(), _1, '\r'))))) == iterEnd
			|| *targetEnd == '\r')
		{
			success = false;
			break;
		}

		if (*targetEnd == '?')
		{
			if ((bodyBegin = find_if(targetEnd + 1, iterEnd, bind(not_equal_to<>(), _1, ' '))) == iterEnd || distance(targetEnd + 1, bodyBegin) || *bodyBegin == '\r')
			{
				success = false;
				break;
			}

			if ((bodyEnd = find_if(bodyBegin + 1, iterEnd, bind(logical_or<>(), bind(equal_to<>(), _1, ' '), bind(equal_to<>(), _1, '\r')))) == iterEnd || *bodyEnd == '\r')
			{
				success = false;
				break;
			}
			hasBody = true;
		}

		if ((iterBegin = find_if(*targetEnd == ' ' ? targetEnd : bodyEnd, iterEnd, bind(not_equal_to<>(), _1, ' '))) == iterEnd || *iterBegin == '\r' || *iterBegin != 'H')
		{
			success = false;
			break;
		}

		if (distance(iterBegin, iterEnd) < HTTPStr.size() || !equal(iterBegin, (iterTemp = iterBegin + HTTPStr.size()), HTTPStr.cbegin(), HTTPStr.cend()) || iterTemp == iterEnd || *iterTemp == '\r')
		{
			success = false;
			break;
		}


		if ((iterBegin = find_if(iterTemp + 1, iterEnd, bind(not_equal_to<>(), _1, ' '))) == iterEnd || *iterBegin == '\r')
		{
			success = false;
			break;
		}


		if ((iterTemp = find(iterBegin + 1, iterEnd, '\r')) == iterEnd)
		{
			success = false;
			break;
		}

		if (distance(iterBegin, iterTemp) != 3 || (!equal(iterBegin, iterTemp, HTTP10.cbegin(), HTTP10.cend()) && !equal(iterBegin, iterTemp, HTTP11.cbegin(), HTTP11.cend())))
		{
			success = false;
			break;
		}

		versionBegin = iterBegin, versionEnd = iterTemp;

		iterBegin = versionEnd;
		iterTemp = find_if(iterTemp, iterEnd, bind(logical_and<>(), bind(not_equal_to<>(), _1, '\r'), bind(not_equal_to<>(), _1, '\n')));
		break;
	}
	if (!success)
		return;

	len = distance(iterBegin, iterTemp);
	if (len == 2)
	{
		if (!equal(lineStr.cbegin(), lineStr.cbegin() + 2, iterBegin, iterTemp))
		{
			success = false;
			return;
		}
		while (distance(iterBegin, iterTemp) != 4)
		{
			if ((iterBegin = find_if(iterTemp, iterEnd, bind(not_equal_to<>(), _1, ' '))) == iterEnd || *iterBegin == '\r')
			{
				success = false;
				break;
			}

			if ((iterTemp = find_if(iterBegin + 1, iterEnd, bind(logical_or<>(), bind(equal_to<>(), _1, ':'), bind(equal_to<>(), _1, '\r')))) == iterEnd || *iterTemp == '\r')
			{
				success = false;
				break;
			}

			headBegin = iterBegin, headEnd = iterTemp;

			if (!hasBody && distance(headBegin, headEnd) == ContentLength.size() && equal(headBegin, headEnd, ContentLength.cbegin(), ContentLength.cend(), bind(equal_to<>(), bind(tolower, _1), _2)))
			{
				if ((iterBegin = find_if(iterTemp + 1, iterEnd, bind(not_equal_to<>(), _1, ' '))) == iterEnd || *iterBegin == '\r' || !isdigit(*iterBegin))
				{
					success = false;
					break;
				}

				if ((iterTemp = find_if_not(iterBegin + 1, iterEnd, bind(logical_and<>(), bind(greater_equal<>(), _1, '0'), bind(less_equal<>(), _1, '9')))) == iterEnd || *iterTemp != '\r')
				{
					success = false;
					break;
				}

				len = -1, num = 1;
				bodyLen = accumulate(make_reverse_iterator(iterTemp), make_reverse_iterator(iterBegin), 0, [&len, &num](auto &sum, auto const ch)
				{
					if (++len)num *= 10;
					return sum += (ch - '0')*num;
				});
			}
			else
			{
				if ((iterBegin = find_if(iterTemp + 1, iterEnd, bind(not_equal_to<>(), _1, ' '))) == iterEnd || *iterBegin == '\r')
				{
					success = false;
					break;
				}

				if ((iterTemp = find(iterBegin + 1, iterEnd, '\r')) == iterEnd)
				{
					success = false;
					break;
				}

				wordBegin = iterBegin, wordEnd = iterTemp;
			}

			iterBegin = iterTemp;

			iterTemp = find_if(iterTemp + 1, iterEnd, bind(logical_and<>(), bind(not_equal_to<>(), _1, '\r'), bind(not_equal_to<>(), _1, '\n')));

			len = distance(iterBegin, iterTemp);

			if (len != 2 && len != 4)
			{
				success = false;
				break;
			}
			if (len == 2 && !equal(lineStr.cbegin(), lineStr.cbegin() + 2, iterBegin, iterTemp))
			{
				success = false;
				break;
			}
		}
	}
	if (!success)
		return;
	if (len == 4)
	{
		if (!equal(lineStr.cbegin(), lineStr.cend(), iterBegin, iterTemp))
		{
			success = false;
			return;
		}
		if (!hasBody)
		{
			if (bodyLen)
			{
				if (distance(iterTemp, iterEnd) >= bodyLen)
				{
					bodyBegin = iterTemp;
					bodyEnd = iterTemp + bodyLen;
				}
				else
				{
					success = false;
				}
			}
		}
	}
	else
		success = false;
	cout << success << '\n';
}



int main()
{
	string str{ "HEAD /inst.exe HTTP/1.1\r\nHost: down.360safe.com\r\nUser-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: zh-cn,zh;q=0.5\r\nAccept-Encoding: gzip,deflate\r\nAccept-Charset: GB2312,utf-8;q=0.7,*;q=0.7\r\nConnection: close\r\n\r\n" };

	parse(str);

	str.assign("POST /test/test HTTP/1.1\r\nHost: 47.98.173.227:8085\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 10\r\n\r\naction=ALL");

	parse(str);

	return 0;
}

